//
// IntrGateManager.cpp
// Usage: Manage all `GATE' in the kernel
// Create Date: 2009-03-18
// Author: MaJiuyue
//
#include "IntrGateManager.h"
#include "io.h"
#include "chips.h"
#include "SCI.h"
#include "bootutil.h"
#include "../schedule/Process.h"

extern "C" void irq_handler(u32 irq, regs_t * regs)
{
	Resource * res = ResourceManager.RequestResource(ServiceCallInterface::ActionManager::ResourceName);
	res->CallSCI(ServiceCallInterface::ActionManager::SCIID_IRQHandler, irq, (u32)regs);
}

extern "C" void calluser();
extern "C" void exception_handler(u32 vec_no, u32 err_code,  regs_t * regs)
{
	Resource * res = ResourceManager.RequestResource(ServiceCallInterface::ActionManager::ResourceName);
	res->CallSCI(ServiceCallInterface::ActionManager::SCIID_ExceptionHandler, vec_no, err_code, (u32)regs);
}

extern "C" void syscall_handler(regs_t * regs)
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ActionManager::ResourceName
	);
	res->CallSCI(
		ServiceCallInterface::ActionManager::SCIID_SyscallHandler,
		(u32)regs
	);
}

CIntrGateManager::CIntrGateManager()
{
	init8259A();
	init8253();
	initIDT();

	Resource *res = new Resource(this);
	ResourceManager.CreateResource(res,
			 ServiceCallInterface::IntrGateManager::ResourceName); 
	res->RegisterInterface(
		(Resource::CallInterface4)&CIntrGateManager::SetGateProperty,
		ServiceCallInterface::IntrGateManager::SCIID_SetGateProperty
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CIntrGateManager::EnableInterrupt,
		ServiceCallInterface::IntrGateManager::SCIID_EnableInterrupt
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CIntrGateManager::DisableInterrupt,
		ServiceCallInterface::IntrGateManager::SCIID_DisableInterrupt
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CIntrGateManager::EnableIRQ,
		ServiceCallInterface::IntrGateManager::SCIID_EnableIRQ
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CIntrGateManager::DisableIRQ,
		ServiceCallInterface::IntrGateManager::SCIID_DisableIRQ
	);
}
void CIntrGateManager::SetGateProperty(int index, u32 type, handler_type handler, u32 privilege)
{
	u32 base = (u32)handler;
	idt[index].offset_low	= base & 0xFFFF;
	idt[index].selector	= SELECTOR_KERNEL_C;
	idt[index].dcount	= 0;
	idt[index].attr	= type | (privilege << 5);
	idt[index].offset_high	= (base >> 16) & 0xFFFF;
}

void CIntrGateManager::EnableIRQ(u32 irq)
{
	u8 mask;
	port_t port;
	
	mask = ~(1<<(irq%8));
	port = irq<8 ? PIC_M_CTLMASK : PIC_S_CTLMASK;

	__asm__ __volatile__("pushf\n\t"
		"inb %%dx, %%al\n\t"
		"and %1, %%al\n\t"
		"outb %%al,%%dx\n\t"
		"popf"
		::"d"(port),"m"(mask)
	);
}

void CIntrGateManager::DisableIRQ(u32 irq)
{
	u8 mask;
	port_t port;
	
	mask = 1<<(irq%8);
	port = irq<8 ? PIC_M_CTLMASK : PIC_S_CTLMASK;

	__asm__ __volatile__("pushf\n\t"
		"inb %%dx, %%al\n\t"
		"and %1, %%al\n\t"
		"outb %%al,%%dx\n\t"
		"popf"
		::"d"(port),"m"(mask):);
}


void CIntrGateManager::init8259A()
{
	_outp(PIC_M_CTL, 0x11);			// Master 8259, ICW1.
	_outp(PIC_S_CTL, 0x11);			// Slave  8259, ICW1.
	_outp(PIC_M_CTLMASK, INT_VECTOR_IRQ0);	// Master 8259, ICW2.
	_outp(PIC_S_CTLMASK, INT_VECTOR_IRQ8);	// Slave  8259, ICW2.
	_outp(PIC_M_CTLMASK, 0x4);		// Master 8259, ICW3.
	_outp(PIC_S_CTLMASK, 0x2);		// Slave  8259, ICW3.
	_outp(PIC_M_CTLMASK, 0x1);		// Master 8259, ICW4.
	_outp(PIC_S_CTLMASK, 0x1);		// Slave  8259, ICW4.
	_outp(PIC_M_CTLMASK, 0xFF);		// Master 8259, OCW1. 
	_outp(PIC_S_CTLMASK, 0xFF);		// Slave  8259, OCW1. 
}

void CIntrGateManager::init8253()
{
	_outp(TIMER_MODE, RATE_GENERATOR);
	_outp(TIMER0, (u8)(TIMER_FREQ/HZ));
	_outp(TIMER0, (u8)((TIMER_FREQ/HZ)>>8));
}

void CIntrGateManager::initIDT()
{
	*((u16*)(&idt_ptr[0])) = IDT_SIZE * sizeof(GATE) - 1;	//IDT limit
	*((u32*)(&idt_ptr[2])) = vir2lin((u32)idt);		//IDT addr

	SetGateProperty(INT_VECTOR_DIVIDE,  DA_386IGate, divide_error, PRIV_KNL);
	SetGateProperty(INT_VECTOR_DEBUG,   DA_386IGate, single_step_exception,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_NMI,     DA_386IGate, nmi,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_BREAKPOINT,	DA_386IGate, breakpoint_exception,	PRIV_USR);
	SetGateProperty(INT_VECTOR_OVERFLOW, DA_386IGate, overflow, PRIV_USR);
	SetGateProperty(INT_VECTOR_BOUNDS, DA_386IGate, bounds_check, PRIV_KNL);
	SetGateProperty(INT_VECTOR_INVAL_OP, DA_386IGate, inval_opcode,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_COPROC_NOT, DA_386IGate, copr_not_available,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_DOUBLE_FAULT, DA_386IGate, double_fault, PRIV_KNL);
	SetGateProperty(INT_VECTOR_COPROC_SEG, DA_386IGate, copr_seg_overrun,		PRIV_KNL);
	SetGateProperty(INT_VECTOR_INVAL_TSS, DA_386IGate, inval_tss, PRIV_KNL);
	SetGateProperty(INT_VECTOR_SEG_NOT, DA_386IGate, segment_not_present,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_STACK_FAULT,	DA_386IGate, stack_exception,		PRIV_KNL);
	SetGateProperty(INT_VECTOR_PROTECTION,	DA_386IGate, general_protection,	PRIV_KNL);
	SetGateProperty(INT_VECTOR_PAGE_FAULT,	DA_386IGate, page_fault,			PRIV_KNL);
	SetGateProperty(INT_VECTOR_COPROC_ERR,	DA_386IGate, copr_error,			PRIV_KNL);

	SetGateProperty(INT_VECTOR_IRQ0 + 0, DA_386IGate, hwint00, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 1, DA_386IGate, hwint01, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 2, DA_386IGate, hwint02, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 3, DA_386IGate, hwint03, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 4, DA_386IGate, hwint04, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 5, DA_386IGate, hwint05, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 6, DA_386IGate, hwint06, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ0 + 7, DA_386IGate, hwint07, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 0, DA_386IGate, hwint08, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 1, DA_386IGate, hwint09, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 2, DA_386IGate, hwint10, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 3, DA_386IGate, hwint11, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 4, DA_386IGate, hwint12, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 5, DA_386IGate, hwint13, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 6, DA_386IGate, hwint14, PRIV_KNL);
	SetGateProperty(INT_VECTOR_IRQ8 + 7, DA_386IGate, hwint15, PRIV_KNL);

	SetGateProperty(INT_VECTOR_SYS_CALL, DA_386IGate, _syscall, PRIV_USR);

	__asm__ __volatile__("lidt %0"::"m"(idt_ptr[0]):);
}

